package com.pz.gulimall.auth.service;

import com.alibaba.fastjson2.JSON;
import com.pz.gulimall.auth.feign.ProductFeign;
import com.pz.gulimall.auth.feign.WareFeign;
import com.pz.gulimall.auth.vo.res.ShopCarItemResVo;
import com.pz.gulimall.common.exception.BizCodeEnum;
import com.pz.gulimall.common.exception.RRException;
import com.pz.gulimall.common.to.SkuHasStockTo;
import com.pz.gulimall.common.to.SkuInfoTo;
import com.pz.gulimall.common.utils.R;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static com.pz.gulimall.auth.constan.CacheConstant.CART_CACHE_NAME;

/**
 * 购物车的购物项缓存到 redis，之后开启定时任务将redis缓存添加到数据库持久存储
 * key规则：`prefix_loginId`
 */
@Service
public class ShopCarService {

    @Autowired
    private ProductFeign productFeign;
    @Autowired
    private ThreadPoolExecutor executor;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private WareFeign wareFeign;


    /**
     * 添加购物项
     * @param loginId
     * @param skuId
     * @param count 为复数表示减去对应的数量
     * @return
     */
    @Transactional
    @CachePut(value = CART_CACHE_NAME, key = "#loginId")
    public List<ShopCarItemResVo> addItem(String loginId, Long skuId, int count) throws ExecutionException, InterruptedException {
        //先获取
        List<ShopCarItemResVo> list = cacheService.getCartItems(loginId);

        List<ShopCarItemResVo> collect = list.stream().peek(item -> {
            if(item.getSkuId().longValue() == skuId){
                item.setCount(count);
            }
            item.setTotalPrice();
        }).collect(Collectors.toList());
        List<ShopCarItemResVo> exist = collect.stream().filter(item -> item.getSkuId().longValue() == skuId).collect(Collectors.toList());

        if(exist.isEmpty()){
            // 当前添加的商品不在购物车中
            CompletableFuture<R> task1 = CompletableFuture.supplyAsync(() -> {
                R ans1;
                try {
                    ans1 = productFeign.info(skuId);
                } catch (Exception e) {
                    throw new RRException(BizCodeEnum.AUTH_RPC_PRODUCT_FAILED.getMsg(), BizCodeEnum.AUTH_RPC_PRODUCT_FAILED.getCode());
                }
                if(ans1.getCode() != 0){
                    throw new RRException(ans1.getMsg(), ans1.getCode());
                }
                return ans1;
            }, executor);
            CompletableFuture<R> task2 = CompletableFuture.supplyAsync(() -> {
                R ans2;
                try {
                    ans2 = productFeign.getSaleAttr(skuId);
                } catch (Exception e) {
                    throw new RRException(BizCodeEnum.AUTH_RPC_PRODUCT_FAILED.getMsg(), BizCodeEnum.AUTH_RPC_PRODUCT_FAILED.getCode());
                }
                if(ans2.getCode() != 0){
                    throw new RRException(ans2.getMsg(), ans2.getCode());
                }
                return ans2;
            }, executor);

            CompletableFuture.allOf(task1, task2);
            R ans1 = task1.get();
            R ans2 = task2.get();

            ShopCarItemResVo vo = new ShopCarItemResVo();
            BeanUtils.copyProperties(JSON.to(SkuInfoTo.class, ans1.get("skuInfo")), vo);
            vo.setLoginId(loginId);
            vo.setSaleAttrs(ans2.getDataArray(String.class));
            vo.setCount(count);
            vo.setTotalPrice();
            collect.add(vo);
        }

        return collect;
    }

    @Transactional
    @CachePut(value = CART_CACHE_NAME, key = "#loginId")
    public List<ShopCarItemResVo> delItem(String loginId, List<Long> skuIds) {
        //异步任务1：获取缓存，过滤掉删除的购物项
        CompletableFuture<List<ShopCarItemResVo>> task1 = CompletableFuture.supplyAsync(() -> cacheService.getCartItems(loginId), executor).thenApplyAsync((res) -> {
            Set<Long> set = new HashSet<>(skuIds);
            //过滤掉含有 skuIds 的购物项
            return res.stream().filter(item -> !set.contains(item.getSkuId())).peek(ShopCarItemResVo::setTotalPrice).collect(Collectors.toList());
        }, executor);

        //异步任务2：缓存清除
        CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> {
            cacheService.DelCache(loginId);
        }, executor);

        //异步任务3：数据库同步删除
        CompletableFuture<Void> task3 = CompletableFuture.runAsync(() -> {
            // 数据库同步删除
            try {
                R ans = productFeign.del(loginId, skuIds);
                if (ans.getCode() != 0) {
                    throw new RRException(ans.getMsg(), ans.getCode());
                }
            } catch (Exception e) {
                throw new RRException(BizCodeEnum.AUTH_RPC_PRODUCT_FAILED.getMsg(), BizCodeEnum.AUTH_RPC_PRODUCT_FAILED.getCode());
            }
        }, executor);

        CompletableFuture.allOf(task1, task2, task3);

        try {
            //将剩余的数据同步到缓存
            return task1.get();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RRException(BizCodeEnum.THREAD_EXCEPTION.getMsg(), BizCodeEnum.THREAD_EXCEPTION.getCode());
        }
    }

    public List<ShopCarItemResVo> getCartItems(String loginId) {
        List<ShopCarItemResVo> cartItems = cacheService.getCartItems(loginId);
        //查询库存
        List<Long> skuIds = cartItems.stream().map(ShopCarItemResVo::getSkuId).collect(Collectors.toList());;
        try {
            R r = wareFeign.hasStockBySkuId(skuIds);
            if(r.getCode() == 0){
                List<SkuHasStockTo> hasStocks = r.getDataArray(SkuHasStockTo.class);
                Map<Long, Boolean> map = new HashMap<>();
                hasStocks.forEach(item->{
                    map.put(item.getSkuId(), item.getHasStock());
                });
                cartItems.forEach(item -> item.setHasStock(map.get(item.getSkuId())));
            }
        } catch (Exception e) {
            throw new RRException(BizCodeEnum.AUTH_RPC_WARE_FAILED, e.getMessage());
        }
        return cartItems;
    }

    public List<ShopCarItemResVo> getFromCache(String loginId) {
        return cacheService.getFromCache(loginId);
    }

}
